#include "Common.h"
#include "Application.h"
#include "D3D10Graphics.h"
#include "Win32Input.h"
#include "Win32Time.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d10.h>
#include <d3dx10.h>

#define CLASS_NAME "GenBenWindow"

namespace GenBen {

Application* globalApplication = NULL;
HWND globalWindowHandle = NULL;

uint globalWidth, globalHeight, globalScale;
bool globalFullscreen, globalLcd;

D3D10Graphics* graphics = NULL;
Win32Input* input = NULL;
Win32Time* time = NULL;

void criticalError(string message)
{
	MessageBox(NULL, message.c_str(), "Critical error!", MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
	exit(0);
}

LRESULT CALLBACK wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
    HDC hdc;

	if(input)
		if(input->process(hWnd, message, wParam, lParam))
			return 0;

    switch (message) 
    {
        case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

Application::Application(const string& title, uint width, uint height, uint scale, bool fullscreen, bool lcd)
{
	ASSERT(width >= 320 && width <= 1600);
	ASSERT(height >= 240 && height <= 1200);
	ASSERT(scale >= 1 && scale <= 4);

	if(globalApplication)
		criticalError("Instance of Application already exists");

	globalWidth = width;
	globalHeight = height;
	globalScale = scale;
	globalFullscreen = fullscreen;
	globalLcd = lcd;

	// Register class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = wndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = NULL;
    wcex.hIcon = NULL;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = CLASS_NAME;
    wcex.hIconSm = NULL;
    if(!RegisterClassEx(&wcex))
        criticalError("Unable to register window class");

    // Create window
	DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
    RECT rc = { 0, 0, width, height };
    AdjustWindowRect(&rc, style, FALSE);
	globalWindowHandle = CreateWindow( CLASS_NAME, title.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, 
		rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, NULL, NULL);

	if(!globalWindowHandle)
		criticalError("Unable to create window");
	
	ShowWindow(globalWindowHandle, SW_SHOW);
	UpdateWindow(globalWindowHandle);

	graphics = NULL;

	globalApplication = this;

	time = new Win32Time();
}

Application::~Application()
{
	if(graphics) delete graphics;
	if(input) delete input;
	if(time) delete time;

	UnregisterClass(CLASS_NAME, NULL);
}

void Application::run()
{
	init();

	MSG msg = {0};
    while(WM_QUIT != msg.message)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
			time->update();

			if(input)
				input->update();

            update();
			render();
        }
    }

	close();
}

Graphics* Application::getGraphics()
{
	if(graphics == NULL)
		graphics = new D3D10Graphics(globalWindowHandle, globalWidth, globalHeight, globalScale, 60, globalFullscreen, globalLcd);

	return (Graphics*)graphics;
}

Input* Application::getInput()
{
	if(input == NULL)
		input = new Win32Input();

	return (Input*)input;
}

Time* Application::getTime()
{
	return (Time*)time;
}

};